#include <unistd.h>
#include <string.h> // memset()、bzero()
// #include <errno.h>

#include "./EPollPoller.h"
#include "../utility/Logger.h"
#include "./Channel.h"

// TODO：使用enum class表示channel状态
const int kNew = -1;    // channel未添加至Poller；channel的成员status_初始化为-1
const int kAdded = 1;   // channel已添加至Poller
const int kDeleted = 2; // channel已从Poller删除

// epoll_create1()是epoll_create()改进
// EPOLL_CLOEXEC：再fork-exec，子进程不会继承epollfd即子进程关闭epollfd
// this->kInitReventVectorSize可以编译过
EPollPoller::EPollPoller(EventLoop *loop)
    : Poller(loop),
      epollFd_(::epoll_create1(EPOLL_CLOEXEC)),
      revents_(kInitReventVectorSize)
{
    // 初始化列表后再检查
    if (this->epollFd_ < 0)
    {
        LOG_FATAL("epoll_create error:%d \n", errno);
    }
}

EPollPoller::~EPollPoller()
{
    ::close(this->epollFd_);
}

// channel->loop->poller进行update和remove->epoll_ctl()
void EPollPoller::updateChannel(Channel *chan)
{
    // __FUNCTION__：编译器提供宏
    LOG_INFO("func=%s => fd=%d events=%d status=%d\n", __FUNCTION__, chan->getFd(), chan->getEvents(), chan->getStatus());

    // 类似状态机状态转换
    const int status = chan->getStatus();
    if (status == kNew || status == kDeleted)
    {
        if (status == kNew)
        {
            int fd = chan->getFd();
            this->fdToChanMap_[fd] = chan;
        }
        // status == kDeleted

        // 注意：先添加再设置状态；实际上顺序应该无关紧要，
        // TODO: 需要成功才设置状态；一些系统调用如close()也可以检验返回值完善
        this->updateChannel(EPOLL_CTL_ADD, chan);
        chan->setStatus(kAdded);
    }
    else // status == kAdded
    {
        // int fd = chan->getFd();
        if (chan->isNoneEvent())
        {
            chan->setStatus(kDeleted);
            this->updateChannel(EPOLL_CTL_DEL, chan);
        }
        else
        {
            this->updateChannel(EPOLL_CTL_MOD, chan);
        }
    }
}

// epoll_ctl add/mod/del
void EPollPoller::updateChannel(int op, Channel *chan)
{
    int fd = chan->getFd();

    epoll_event event{};
    // ::memset(&event, 0, sizeof(event));
    bzero(&event, sizeof(event));

    event.events = chan->getEvents();
    event.data.fd = fd;
    event.data.ptr = chan; // 注意：携带用户数据

    // 注意：
    // 删除失败，可以不用，错误日志
    // 增加修改失败，致命错误日志
    if (::epoll_ctl(epollFd_, op, fd, &event) < 0)
    {
        // OPTIMIZE: 细化操作错误日志
        if (op == EPOLL_CTL_DEL)
        {
            LOG_ERROR("epoll_ctl del error:%d\n", errno);
        }
        else if (op == EPOLL_CTL_ADD)
        {
            LOG_FATAL("epoll_ctl add error:%d\n", errno);
        }
        else if (op == EPOLL_CTL_MOD)
        {
            LOG_FATAL("epoll_ctl add error:%d\n", errno);
        }
    }
}

void EPollPoller::removeChannel(Channel *chan)
{
    LOG_INFO("func=%s => fd=%d\n", __FUNCTION__, chan->getFd());

    // 1. 从poller删除
    int fd = chan->getFd();
    this->fdToChanMap_.erase(fd);

    // 2. 从epoll删除
    int status = chan->getStatus();
    if (status == kAdded)
    {
        this->updateChannel(EPOLL_CTL_DEL, chan);
    }
    chan->setStatus(kNew);
}

// 传指针，loop调用该方法等待/开启事件循环，指针获取到返回值进一步操作
Timestamp EPollPoller::poll(const int timeoutMs, ChannelVector *activeChans)
{
    // 该方法调用很频繁，实际上应该用LOG_DEBUG输出日志更为合理，高并发时再关闭
    LOG_INFO("func=%s => fd total count:%lu\n", __FUNCTION__, this->fdToChanMap_.size());

    // 为了扩容用容器封装，调用需要原始地址则获取容器首元素地址
    int reventsNum = ::epoll_wait(this->epollFd_, &*(this->revents_.begin()),
                                  static_cast<int>(this->revents_.size()), timeoutMs);

    int saveErrno = errno;
    Timestamp now(Timestamp::now());
    if (reventsNum > 0)
    {
        LOG_INFO("%d events happend\n", reventsNum);

        this->fillActiveChannels(reventsNum, activeChans);

        if (reventsNum == this->revents_.size()) // 需要扩容则扩容，LT模式下一轮还会继续处理
        {
            this->revents_.resize(this->revents_.size() * 2);
        }
    }
    else if (reventsNum == 0)
    {
        LOG_DEBUG("%s timeout!\n", __FUNCTION__);
    }
    else
    {
        if (saveErrno != EINTR) // 外部中断应该继续epoll_wait()，这里是其他错误
        {
            errno = saveErrno;

            LOG_ERROR("EPollPoller::poll() error!");
        }
    }
    return now;
}

// 事件到达处理epoll_wait()返回的内容，填写就绪activeChannel通过poll()参数指针返回给loop进一步处理
void EPollPoller::fillActiveChannels(int reventsNum, ChannelVector *activeChans) const
{
    for (int i = 0; i < reventsNum; ++i)
    {
        Channel *chan = static_cast<Channel *>(this->revents_[i].data.ptr); // 获取监听前填的用户数据
        chan->setRevents(this->revents_[i].events);
        activeChans->push_back(chan); // EventLoop就拿到了它的Poller返回的所有发生事件的channel列表了
    }
}